#pragma once
#include "Parser.h"
#include "DesignEntity.h"
#include "Node.h"
#include <iostream>

Parser::Parser(const char* fileName, AST &ast, ProcTable &procTable, VarTable &varTable, Follows &follows, Parent &parent){
	std::FILE *file = std::fopen(fileName, "rb");
    std::fseek(file, 0, SEEK_END);
    sourceCode.resize(std::ftell(file));
    std::rewind(file);
    std::fread(&sourceCode[0], 1, sourceCode.size(), file);
    std::fclose(file);
	universalRegex = std::regex(UNIVERSAL);
	regexIterator = std::regex_iterator<std::string::iterator>(sourceCode.begin(), sourceCode.end(), universalRegex);
	rend = std::regex_iterator<std::string::iterator>();
	stmtNumber = 0;
	this->ast = &ast;
	this->procTable = &procTable;
	this->varTable = &varTable;
	this->follows = &follows;
	this->parent = &parent;
}

Symbol Parser::getSymbol(std::string str) {
	Symbol symbol = nosymbol;
	std::regex regex;
	regex = std::regex(INTEGERSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = integersym;
	}
	regex = std::regex(NAMESYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = namesym;
	}
	regex = std::regex(LPARENSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = lparensym;
	}
	regex = std::regex(RPARENSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = rparensym;
	}
	regex = std::regex(BEGINSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = beginsym;
	}
	regex = std::regex(ENDSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = endsym;
	}
	regex = std::regex(SEMICOLONSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = semicolonsym;
	}
	regex = std::regex(EQUALSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = equalsym;
	}
	regex = std::regex(PLUSSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = plussym;
	}
	regex = std::regex(MINUSSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = minussym;
	}
	regex = std::regex(TIMESSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = timessym;
	}
	regex = std::regex(PROCEDURESYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = proceduresym;
	}
	regex = std::regex(CALLSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = callsym;
	}
	regex = std::regex(IFSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = ifsym;
	}
	regex = std::regex(THENSYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = thensym;
	}
	regex = std::regex(ELSESYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = elsesym;
	}
	regex = std::regex(WHILESYM);
	if (regex_match(str.begin(), str.end(), regex)) {
		symbol = whilesym;
	}
	return symbol;
}

bool Parser::accept(Symbol s){
	if (regexIterator != rend) {
		currentString = regexIterator->str();
		if (getSymbol(currentString)==s) {
			++regexIterator;
			return true;
		}
	return false; 
	} else {
		return false;
	}
}

void Parser::expect(Symbol s){
	if (accept(s)) {
	} else {
		error();
	}
}

Node* Parser::acceptVarName() {
	if (accept(namesym)) {
		int varIndex = varTable->addVar(currentString,stmtNumber);
		Variable* varDE = new Variable(varIndex);
		Node* varNode = new Node( varDE,stmtNumber);
		return varNode;
	} else {
		return NULL;
	}
	return NULL;
}

Node* Parser::expectVarName() {
	Node* varNode;
	if (varNode=acceptVarName()) {
		return varNode;
	} else {
		error();
	}
}

Node* Parser::acceptConstValue() {
	if (accept(integersym)) {
		Constant* constDE = new Constant(atoi(currentString.c_str()));
		Node* constNode = new Node( constDE,stmtNumber);
		return constNode;
	} else {
		return NULL;
	}
}

Node* Parser::expectConstValue() {
	Node* constNode;
	if (constNode = acceptConstValue()) {
	} else {
		error();
	}
	return constNode;
}

Node* Parser::acceptProcName() {
	if (accept(namesym)) {
		int procIndex = procTable->addProc(currentString);
		Node* procNode = new Node( new Procedure(procIndex),-1);
		return procNode;
	} else {
		return NULL;
	}
}

Node* Parser::expectProcName() {
	Node* procNode;
	if (procNode=acceptProcName()) {
	} else {
		error();
	}
	return procNode;
}

Node* Parser::expectProgram() {
	Node* programNode = new Node(new Program(),-1);
	programNode->addChildNode(expectProcedure());
	Node* procNode;
	while (procNode=acceptProcedure()) {
		programNode->addChildNode(procNode);
	}
	if (regexIterator != rend) {//there are 'leftovers'
		error();
	}
	return programNode;
}

Node* Parser::acceptProcedure(){
	if (accept(proceduresym)) {
		Node* procNode = expectProcName();
		expect(beginsym);
		procNode->addChildNode(expectStmtList(-1));
		expect(endsym);
		return procNode;
	}	else {
		return NULL;
	}
}

Node* Parser::expectProcedure(){
	Node* procNode;
	if (procNode=acceptProcedure()) {
		return procNode;
	} else {
		error();
	}
	return procNode;
}

Node* Parser::expectStmtList(int inheritedStmtNumber) {
	Node* stmtListNode;
	stmtListNode=new Node( new StmtList(), inheritedStmtNumber); 
	int stmtNumber1;
	int stmtNumber2;
	Node* stmtNode = expectStmt();
	parent->addParent(stmtListNode->getStmtNumber(), stmtNode->getStmtNumber());
	stmtNumber1=stmtNode->getStmtNumber();
	stmtListNode->addChildNode(stmtNode);
	while (stmtNode=acceptStmt()) {
		stmtNumber2=stmtNode->getStmtNumber();
		parent->addParent(stmtListNode->getStmtNumber(), stmtNode->getStmtNumber());
		follows->addFollows(stmtNumber1,stmtNumber2);
		stmtListNode->addChildNode(stmtNode);
		stmtNumber1=stmtNode->getStmtNumber();
	}
	return stmtListNode;
}

Node* Parser::acceptStmt(){
	Node* stmtNode;
	stmtNumber++;
	if (accept(callsym)) {
		expect(namesym);
		stmtNode = new Node( new Call(procTable->addProc(currentString)), stmtNumber); 
		expect(semicolonsym);
	}	else if (accept(whilesym)) {
		stmtNode = new Node( new While(), stmtNumber);
		stmtNode->addChildNode(expectVarName());
		expect(beginsym);
		stmtNode->addChildNode(expectStmtList(stmtNumber));
		expect(endsym);
	}	else if (accept(ifsym)) {
		int inheritedStmtNumber = stmtNumber;
		stmtNode = new Node( new If(), stmtNumber);
		stmtNode->addChildNode(expectVarName());
		expect(beginsym);
		stmtNode->addChildNode(expectStmtList(inheritedStmtNumber));
		expect(endsym);
		if (accept(elsesym)) {
			expect(beginsym);
			stmtNode->addChildNode(expectStmtList(inheritedStmtNumber));
			expect(endsym);
		}
	}	else if (Node* varNode=acceptVarName()) {
		stmtNode = new Node(new Assign(), stmtNumber);
		stmtNode->addChildNode(varNode);
		expect(equalsym);
		stmtNode->addChildNode(expectExpr());
		expect(semicolonsym);
	}	else {
		stmtNumber--; //if there's no statement to accept, undo the stmtNumber addition
		return NULL;
	}
	return stmtNode;
}

Node* Parser::expectStmt(){
	Node* stmtNode;
	if (stmtNode=acceptStmt()) {
	} else {
		error();
	}
	return stmtNode;
}

Node* Parser::expectExpr(){
	Node* exprNode = expectTerm();
	bool plus;
	bool min;
	while ((plus=accept(plussym))||(min=accept(minussym))) {
		Node* opNode;
		if (plus) {
			opNode = new Node(new Plus(), stmtNumber);
		} else if (min) {
			opNode = new Node(new Minus(), stmtNumber);
		}
		opNode->addChildNode(exprNode);
		opNode->addChildNode(expectTerm());
		exprNode = opNode;
		plus=min=false;
	}
	return exprNode;
}

Node* Parser::expectTerm(){
	Node* termNode = expectFactor();
	while (accept(timessym)) {
		Node* opNode = new Node( new Times(), stmtNumber);
		opNode->addChildNode(termNode);
		opNode->addChildNode(expectFactor());
		termNode = opNode;
	}
	return termNode;
}

Node* Parser::expectFactor(){
	Node* factorNode;
	if (accept(lparensym)) {
		factorNode=expectExpr();
		expect(rparensym);
	} else if (factorNode=acceptVarName()) {
	} else if (factorNode=acceptConstValue()) {
	} else {
		error();
	}
	return factorNode;
}

void Parser::error(){
	throw CustomException(SPA_PARSE_ERROR, "Error parsing code");
}

void Parser::buildPKB(){
	Node* root = expectProgram();
	ast->setRoot(root);
	setSubtreeNodeID(root, 0);
}

int Parser::setSubtreeNodeID(Node* node, int nodeID) {
	node->setNodeID(nodeID);
	if (node->getDesignEntity()->getType()==PROCEDURE) {
		Procedure* proc = dynamic_cast<Procedure*>(node->getDesignEntity());
		procTable->addProcNodeID(proc->getIndex(), nodeID);
	} else if (node->getDesignEntity()->getType()==VARIABLE) {
		Variable* var = dynamic_cast<Variable*>(node->getDesignEntity());
		varTable->addVarNodeID(var->getIndex(), nodeID);
	}
	vector<Node*> children = node->getChildren();
	for (int i = 0 ;i <children.size(); i++) {
		 nodeID=setSubtreeNodeID(children[i], ++nodeID);
	}
	return nodeID;
}
